/**
 * UserDefinedColourScheme.java
 * colourmap
 *
 * @author Mar 11, 2008 Alan W.F. Boyd
 */
package com.googlecode.colourmap.colours;

import java.awt.Color;

import javax.swing.JColorChooser;
import javax.swing.JOptionPane;

import com.googlecode.colourmap.ColourMapUtils;
import com.googlecode.colourmap.ColourSchemeKey;


/**
 * This class allows the user to dynamically specify the colours to be used
 * for the colour map, as well as the maximum and minimum data values.
 * @author Alan W.F. Boyd
 *
 */
public class UserDefinedColourScheme implements IColourScheme
{
	/** The minimum data point */
	private float min = 0;
	
	/** The maximum data point */
	private float max = 0;
	
	/** The colour corresponding to the minimum data point */
	private Color minColour = Color.BLUE;
	
	/** The colour corresponding to the maximum data point */
	private Color maxColour = Color.RED;
	
	/** The key that shows colours and data points */
	private ColourSchemeKey k;
	
	/**
	 * The constructor
	 */
	public UserDefinedColourScheme()
	{
		k = new ColourSchemeKey(min, max);
		queryUserforColours();
		k.setColourScheme(this);
		k.setVisible(true);
	}
	
	/**
	 * Returns the outline colour of the data points
	 * @return the colour of the outline of the data points
	 */
	public Color getOutlineColour()
	{
		return Color.black;
	}
	
	/**
	 * Given a data value, this function returns the colour of that point.
	 * The colour is based on the maximum and minimum colours specified by the user.
	 * @param f the data value
	 * @return a colour that corresponds to the supplied data value
	 */
	public Color getColour(float f)
	{
		if (f <= min)
			return minColour;
		else if (f >= max)
			return maxColour;
		
		// Determine the difference between min/max colours and values
		int[] minColourComponents = ColourMapUtils.getRGBComponents(minColour);
		int[] maxColourComponents = ColourMapUtils.getRGBComponents(maxColour);
		int[] componentDifferences = {maxColourComponents[0] - minColourComponents[0], maxColourComponents[1] - minColourComponents[1], maxColourComponents[2] - minColourComponents[2]};
		float minmaxValuesDifference = max - min;
		
		// Determine what difference in colour components is caused for each change in value
		float[] colourComponentChangePerValue = {componentDifferences[0]/minmaxValuesDifference, componentDifferences[1]/minmaxValuesDifference, componentDifferences[2]/minmaxValuesDifference};
		
		// Determine what change in colour components has taken place
		float changeInValue = (f - min);
		int[] colourComponentChange = new int[3];
		colourComponentChange[0] = (int)(colourComponentChangePerValue[0]*changeInValue);
		colourComponentChange[1] = (int)(colourComponentChangePerValue[1]*changeInValue);
		colourComponentChange[2] = (int)(colourComponentChangePerValue[2]*changeInValue);
		
		return new Color(minColour.getRed() + colourComponentChange[0], minColour.getGreen() + colourComponentChange[1], minColour.getBlue() + colourComponentChange[2]);
	}

	/**
	 * Resets the maximum data value in a data set
	 * @param f the new maximum data value
	 */
	public void setMax(float f)
	{
		queryUserforMaxValue(f+"");
		max = f;
		
		k.setMax(f);
		k.repaint();
	}

	/**
	 * Resets the minimum data value in a data set
	 * @param f the new minimum data value
	 */
	public void setMin(float f)
	{
		queryUserforMinValue(f+"");
		min = f;
		k.setMin(f);
		k.repaint();
	}
	
	/**
	 * Queries the user for what colours to use for minimum and maximum values
	 */
	private void queryUserforColours()
	{	
		minColour = JColorChooser.showDialog(k, "Choose Minimum Scale Colour", minColour);
		maxColour = JColorChooser.showDialog(k, "Choose Maximum Scale Colour", maxColour);
	}
	
	/**
	 * Queries the user for the minimum value to use in the colour map
	 * @return the new minimum data value
	 */
	private float queryUserforMinValue(String proposedValue)
	{
		String text = JOptionPane.showInputDialog("What minimum value to use?", proposedValue);

		if (text == null)
			System.exit(0);
			
		// No whitespace
		text = text.replaceAll("\\s", "");
			
		// Convert to number
		try
		{
			return Float.parseFloat(text);
		}
		catch (NumberFormatException e)
		{
			JOptionPane.showMessageDialog(null, "The minimum value '" + text + "' is invalid\nMinimum value should be a number.", "Invalid Minimum Value", JOptionPane.ERROR_MESSAGE);
			queryUserforMinValue(text);
		}
		
		return min;
	}
	
	/**
	 * Queries the user for the maximum value to use in the colour map
	 * @return the new maximum data value
	 */
	private float queryUserforMaxValue(String proposedValue)
	{
		String text = JOptionPane.showInputDialog("What maximum value to use?", proposedValue);

		if (text == null)
			System.exit(0);
			
		// No whitespace
		text = text.replaceAll("\\s", "");
			
		// Convert to number
		try
		{
			return Float.parseFloat(text);
		}
		catch (NumberFormatException e)
		{
			JOptionPane.showMessageDialog(null, "The maximum value '" + text + "' is invalid\nMaximum value should be a number.", "Invalid Maximum Value", JOptionPane.ERROR_MESSAGE);
			queryUserforMaxValue(text);
		}
		
		return max;
	}
}
